/*
X68	Motorola 6800 Cross Assembler 		June 1982
	For CP/M based computer systems
	(with lots of memory and big drives!) 
/*
 *	PERMISSION IS GRANTED FOR THE PUBLIC DOMAIN
 *	NON COMMERCIAL USE OF THIS SOFTWARE.
 *
 *	(C) CHRIS UNDERY 25th OCTOBER 1982
 *	11 MARGARET ST NEWTOWN 2042
 *	SYDNEY, AUSTRALIA
 */

	Chris Undery July 1982
	Peter Ansell Sept 1982
	
	Root Module:		X68 Overlay loader & function library

	Overlay segments:	X68SEG1  Pass 1
				X68SEG2  Pass 2



*/

#include "x68.h"		/* Global definitions file	*/
#define OVERLAY_ADDRESS 0x3c00

/*	Assembler Main Control Loop	*/

main(argc, argv)
int argc;
char **argv;
{
	int (*ptrfn) ();
	struct key *binary();
	struct nlist *lookup(), *install();
	char *tohex(), *getok();
	char *flush(), *purge(), * fault(), *smov();
	char *op(), *numops(), numargs(), *ev(), *evaluate(), *getsub();
	int cnum, todec(), getcomment();
	char command[12];
			
	ptrfn = OVERLAY_ADDRESS;		/* Start of overlay RAM */
	
	printf("\nX68  M6800 Cross Assembler Version A.1     Sept 1982\n");
	if (argc < 2 ) {
		printf("USAGE: A>X68 Filename (options list)\n");
		exit();
	}
	strcpy(filename,argv[1]);
	clrsym();		/* Clear defaults and symbol table */
	if (!load()) 		/* Load mnemonics lookup table	*/
		exit();
	if ( argc >= 2 ) {	/* There are some options to get */
		for (cnum = 2; cnum < argc ; cnum++ ) {
			strcpy(command,argv[cnum]);
			if (!parse(command)) exit();
		}
	}
	if (yanking) yankit();	/* Load an old symbol table */
	if (mloading) {		/* Run Macro processor overlay */
		if (swapin("SEG3X68.OVL",OVERLAY_ADDRESS) != ERROR)
		(*ptrfn)();	/* Execute overlay 	*/
	}
	if (swapin("SEG1X68.OVL",OVERLAY_ADDRESS) != ERROR)
		(*ptrfn)();	/* Execute overlay	*/

	else {
		printf("Aborting\n");
		exit();
	}
	if (swapin("SEG2X68.OVL",OVERLAY_ADDRESS) != ERROR)
		(*ptrfn)();
	else {
		printf("Aborting\n");
		exit();
	}
	if (syming) wrsym();	/* Dump symbol table to UFN.SYM */
	stats();

}

clrsym()	/* Clear up the symbol bucket pointers	*/
{
	int i;
	_allocp = NULL;		/* Initialise memory allocator */
	for (i=0; i < HASHSIZE; i++) hashtab[i] = NULL;
	mloading = decad = lineno = useage = numlines = nb = dec = errors = 0;
	debug = listing = syming = yanking = 0;	/* Default off on all options */
	numbytes = xing = printing = 0;
	strcpy(hexad,"0000");	/* Default to ORG zero */
}

hash(s)		/* Calculate hash bucket start point for symbol */
char *s;
{
	int hashval;

	for (hashval = 0; *s != '\0'; )
		hashval += *s++;
	return(hashval % HASHSIZE);
}

struct nlist *lookup(s)			/* Lookup symbol s in hashtab */
char *s;
{
	struct nlist *np;

	for (np = hashtab[hash(s)]; np != NULL; last = np,  np = np->next)
		if (strcmp(s,np->name) == 0)
			return(np);	/* found symbol	*/
	return(NULL);
}

struct nlist *install(nam,df)		/* Put new symbol into table */
char *nam, *df;
{
	struct nlist *np, *lookup();
	char *strsave(), *alloc();
	int hashval;

	if ((np = lookup(nam)) == NULL) {
		np = alloc(sizeof(*np));
		if (np == NULL)
		   return(NULL);
		if ((np->name = strsave(nam)) == NULL)
		   return(NULL);
		hashval = hash(np->name);
		np->next = hashtab[hashval];
		hashtab[hashval] = np;
		useage += sizeof(*np);	/* Keep statistics */
	} else			/* already there */
		free(np->def);	/* free previous definition */
	if ((np->def = strsave(df)) == NULL)
		return(NULL);
	return(np);
}


strsave(s)	/* Save string in space obtained from dynamic store */
{
	char *p;

	if ((p = alloc(strlen(s)+1)) != NULL)
	  	strcpy(p,s);
	return(p);
}

/* this function performs a binary search of the opcodes table */
/* which was loaded from file OPCODES */

struct key *binary(word)
char *word;
{
	int cond;
	int mid, high, low;
	char kludge;		/* silly flag */
	
	kludge = (word[3] == ' ');
	if (kludge) {
		word[3] = word[4]; /* Move register to 3 */
		word[4] = NULL;
	}
	low = 0;
	high = NKEYS - 1;
	while (low <= high) {
		mid = (low + high) /2;
		if ((cond = strcmp(word,v[mid].keyword)) < 0)
			high = mid - 1;
		else if (cond > 0)
			low = mid + 1;
		else {		/* Extract goodies from table */
			strcpy(inst,v[mid].hexcode);
			strcpy(dest,v[mid].use);
			m4[0] = word[3];
			if (m4[0] != 'A')
			  if (m4[0] != 'B') m4[0] = NULL;
			if (kludge) { /* Restore to spaced mnem */
				word[4] = word[3];
				word[3] = ' ';
				word[5] = NULL;
			}
			return(1);
		}
	}
	return(NULL);
}

/*	Load opcode lookup table, pre sorted to KEYTAB */

load()
{
	int i;
	char buffer[132], nem[BUFSIZ];;
	struct key *bb;
	
	i = 0;
	if (fopen("OPCODES",nem) == ERROR) {
		printf("The OPCODES file is missing !!\n"); 
		return(0);
	}
	while(fgets(buffer,nem)) { 
          bb = &v[i++];
	  sscanf(buffer,"%s\t%s\t%s\n",bb->keyword,bb->hexcode,bb->use);
	}
	fclose(nem);
	return(1);
}

/*	Print assembly statistics */

stats()
{
	printf("\nAssembly complete: ");
	if (errors) printf("%d ERROR(S)\n",errors);
	else printf("no errors\n");
	printf("Source lines processed: %d\n",numlines);
	printf("Last address: %04xH\n",decad);
	printf("Code generated: %d bytes (dec)\n",numbytes);
	printf("Symbol table useage: %d bytes (dec)\n",useage);
	if (syming) printf("Symbol file %s created\n",symname);
	if (xing) printf("Hex file %s created with bias of %04xH\n",hexname,bias);
	if (listing) printf("Print file %s created\n",lstname);
	return(1);
}


/* Convert string representing number in BASE N */

todec(ascii,base)		/* Return val of base string */
char *ascii, base;
{
	unsigned val;		/* will be result */
	char c;

	val = 0;
	if (_bc(_igs(&ascii),base) == ERROR ) return(0);
	while ((c = _bc(*ascii++,base)) != 255)
		val = val * base + c;
	ascii--;
	return(val);
}


/*	Extract substring TOKEN from source string	*/
/*	place in destination string, and truncate 	*/
/*	destination after WIDTH characters have moved	*/

getok(token,from,fldnum,width)
char *token, *from;
int fldnum, width;

{
	int i;
	char inquote;		/* true if on quoted string */

	inquote = 0;
	fldnum--;		/* Gives start occ of string to get */
	if (fldnum == 0) fldnum--;	/* special case fld one */
	i = 0;
	while (i < fldnum) {	/* Index along to fld start	*/
		if ((*from == TAB) || (*from == '\n')) i++;
		if (*from++ == '\0' ) {
			*token = NULL;
			return(0);
		}
	}
	while ((*from != TAB) && (*from != '\n')) {
		if (width-- == 0) {
			*token = '\0';
			return(1);
		}
		if (*from == 0x22 || *from == 0x27) inquote = (~ inquote);
		if (inquote) *token++ = *from++; /* dont alter quotes */
		else *token++ = toupper(*from++);
	}	
	*token = '\0';
}

/*	Scan string for comment delimiter and then move delimter */
/*	and everything following into destination string comment */
/*	ignore case of letters */

getcomment(s)			/* Get COMMENT FLD FROM LINE */
char *s;
{
	while (*s++ ) {		/* search string to comment fld */
		if (*s == ';') {
			strcpy(comment,--s);
			return(1);
		}
	}
	strcpy(comment,"\n");	/* Terminate string with CRLF */
	return(0);
}


tohex(dst,s)			/* Convert decimal to hex */
char *dst;
int s;
{
	sprintf(dst,"%04x",s);	/* 4 digit precision required */
}

smov(dest,source,num)	/* MOV a string partial to dest string */
char *dest, *source;
int num;
{
	while ((*dest++ = *source++) && (num-- != 0));
	*--dest = '\0';
}


parse(option)	/* Parse the assembly time options */
char *option;
{

	if (*option != '-' ) {		/* Invalid command format */
		printf("Bad option syntax: %s\n",option);
		return(0);
	}
	switch (option[1])  {		/* now determine command */
	case 'Y': { yanking = 1;	/* Yank symbol file before start */
		    strcpy(symname,&option[2]);
		    strcat(symname,".SYM");
		    return(1);
		}
	case 'W': { syming = 1;		/* Write symbol file at end */
		    strcpy(symn,&option[2]);
		    strcat(symn,":");
		    strcat(symn,filename);
		    strcat(symn,".SYM");
		    return(1);
		}
	case 'L': { printing = 1;	/* Output to list device */
		    return(1);
		}
	
	case 'P': { strcpy(lstname,&option[2]);
		    strcat(lstname,":"); /* drive name terminator */
		    strcat(lstname,filename);
		    strcat(lstname,".PRN");
		    listing = 1;
		    return(1);		/* Create list file name */
		}
	case 'M': mloading = 1;		/* Enable the pre-processor */
		  return(1);

	case 'D': debug = 1;		/* dont erase tmp file at end */
		  return(1);

	case 'O': if (!ev(&option[2])) {
			printf("Cannot fwd ref on origin switch\n");
			exit();
		  }
		  tohex(hexad,dec); /* else convert result to hex */
		  return(1);

	case 'H': xing = 1;		/* Enable hex file output */
		  strcpy(hexname,&option[2]);
		  strcat(hexname,":");
		  strcat(hexname,filename);
		  strcat(hexname,".HEX");
		  return(1);
	}
	printf("Bad option: <%c>\n",option[1]);
	return(0);			/* force an abort */
}

/*	Write Symbol Table to UFN.SYM		*/
/*	For later re-reading			*/

wrsym()			/* WRite symbol table */
{

	char symb[BUFSIZ];
	struct nlist *tp, *lookup();		/* table pointers */
	int bucket, num;				/* Bucket pointer */

	if (fcreat(symn,symb) == ERROR) {	/* Cannot create */
		printf("Cannot create symbol file: DISK FULL\n");
		exit();
	}
	bucket = num = 0;
	while (bucket < HASHSIZE) {	/* Loop through symbol table */
		for (tp = hashtab[bucket]; tp != NULL; tp = tp->next) {
			fprintf(symb,"%s\t%s\t\n",tp->def,tp->name);
		}
	bucket++;			/* Next bucket */
	}
	putc(0x1a,symb);		/* Close symbol file */
	fflush(symb);
	fclose(symb);
	return(1);
}

/* delete an entry from the symbol table */

purge(symbol)
char *symbol;
{
	struct nlist **prev_link, *tp, *lookup();
	int bucket, num;

	bucket = num = 0;
	if ((tp = lookup(symbol)) == NULL) {
		printf("**WARNING: non existent symbol <%s> no purge\n");
	}
	else {		/* remove this bugger from linked list */
		prev_link = last;
		*prev_link = tp->next;
		free(tp);
		useage -= sizeof(*tp);
	}
	return(1);
}

flush()		/* flush all local symbols from the symbol table */
{

	struct nlist *tp, **back;
	int bucket, num;				/* Bucket pointer */
	char temp[10];

	bucket = num = 0;
	while (bucket < HASHSIZE) {	/* Loop through symbol table */
		for (tp = hashtab[bucket]; tp != NULL; back = tp,tp = tp->next) {
			strcpy(temp,tp->def);
			if (temp[0] < 'A') {	/* if graphic not alpha */
			   *back = tp->next;
			   useage -= sizeof(*tp);
			   free(tp);
			}
		}
	bucket++;			/* Next bucket */
	}
	return(1);
}

/*	Load an old symbol file into the symbol table */
/*	to allow linkage to other modules		*/

yankit()		/* Load symbol file */
{
	char  *symbols, *install(); /* Structure pointers */
	
	if (fopen(symname,ibuf) == ERROR ) {
		printf("Cannot open: %s\n",symname);
		exit();
	}
	printf("Loading %s\n",symname);	/* indicate doing something */
	while (fgets(temp,ibuf)) {	/* Loop reading symbols */
		getok(hexad,temp,1,6);
		getok(label,temp,2,9);	/* Extract from buffer */
		if ((symbols = install(label,hexad)) == NULL ) {
			printf("SYM FILE LOAD HAS BLOWN MY TABLE SPACE BOSS");
			exit();
		}
	}
	fclose(ibuf);	/* Free up buffer for next overlay */
}


/*	Calculate the number of operators in expression */

numops(x)			/* Get # of + - div mult */
char *x;
{
	int  j;

	j = 0;			/* default to no operators */
	while (*x) if (op(*x++)) j++;
	return(j);
}

op(c)				/* Determine if character is operator */
char c;
{
	switch (c) {		/* compare most common first for speed */
		
		case '+':
		case '-':
		case '/':
		case '*':
		case '%':
		case '>':
		case '<':
		case '|':
		case '&': return(1);
			  break;
		default : return(0);
	}
}

numargs(s)			/* Get # of commas in an expression */
char *s;	/* string address */
{
	int i;			/* Argument counter */
	i = 0;
	while (*s) if (*s++ == ',' ) i++;
	return(i+1);		/* there is one more than num of , */
}

/* Evaluate left or right sub expression of a compound operand */

ev(exp)			/* Evaluate expression */
char *exp;
{
	int values[10], j, val, num;
	struct nlist *ep, *lookup(); /* Symbol table pointers */

	if (exp[0] == '$') {		/* Location counter reference */
		dec = decad;		/* Simple one */
		return(1);
	}
	if (exp[0] == '#') strcpy(subexp,&exp[1]);
	else 
		strcpy(subexp,&exp[0]);
	if (isdigit(subexp[0])) {	/* We have literal expression */

		j = strlen(subexp);
		switch (subexp[--j]) {	/* Find Radix */	
	
		case 'B':	subexp[j] = NULL; /* Binary */
				dec = todec(subexp,2); /* Radix two */
				return(1);
			
		case 'O':
		case 'Q':	subexp[j] = NULL; /* Octal */
				dec = todec(subexp,8);
				return(1);

		case 'H':	subexp[j] = NULL; /* Hex */
				dec = todec(subexp,16);
				return(1);
		
		default:	dec = todec(subexp,10); /* Decimal string */
				return(1);
		} 
	} 		/* End of Numeric literals */

	if ((subexp[0] == 0x22) || (subexp[0] == 0x27)) { /* Quoted */
		dec = subexp[1];	/* Get ordinal value of char */
		return(1);
	}
	if ((ep = lookup(subexp)) == NULL ) return(0);
	dec = todec(ep->def,16); /* Convert symbol table entry to dec */
	return(1);
}

/* Evaluate the operand expression. It may be a simple or complex 
 * expression. the function determines if the expression is complex
 * and if so, splits it into a left and right parts. the two parts 
 * are then separately resolved and finally the they are combined via
 * a case switch on the operator . The evaluator can fail with invalid
 * result if an invalid literal is converted to decimal, as the _igs
 * function does not return a status indicating validity of the literal
 * ideally, the evaluator should not just peform a left right split as
 * this prevents it from handling expressions with more than two 
 * components.. this is an area for much improvemment
 */

evaluate(exp)
char *exp;		/* Expression pointer */
{
	int acc1, acc2;			/* Evaluation accumulators */
	char done, ops;
	char res1, res2;		/* result of ev */

	ops = numops(exp);		/* Find number of operators */
	if (ops == 0) return(ev(exp));	/* Normal simple expression */
	done = 0;			/* Else here we go */
	acc1 = acc2 = xptr = 0;		/* Clear accumulators */

	getsub(exp);	/* Split left most two args */
	res1 =ev(xp1);
	acc1 = dec;	/* up to caller to handle the problem */
	res2 = ev(xp2);
	acc2 = dec;
	switch (operator) {	/* decide on operation */
			
		case '+' : acc1 +=  acc2; /* Addition */
			   break;
		case '-' : acc1 -=  acc2; /* Subtraction */
			   break;
		case '/' : acc1 /=  acc2; /* Division */
			   break;
		case '*' : acc1 *=  acc2; /* Multiplication */
			   break;
		case '%' : acc1 %=  acc2; /* Mod */
			   break;
		case '<' : acc1 = (acc1 << acc2); /* shift left */
			   break;
		case '>' : acc1 = (acc1 >> acc2); /* shift right */
			   break;
		case '|' : acc1 = (acc1 | acc2);  /* logical OR */
			   break;
		case '&' : acc1 = (acc1 & acc2);  /* logical AND */
			   break;

		default  : acc1 = 0;
			   fault(1);	/* invalid operator */
			   res1 = 0;
	}
	dec = acc1;
	if ((!res1) || (!res2)) return(0);
	return(1);	/* trap error in either argument */
}

/*	Split the next two left most arguments from expression */

getsub(x)			/* Passed the address of operand */
char x[];
{

	int s;

	s = xptr = 0;
	while ((!op(x[s])) && (x[s] != ' ')) {
		 s++;
	}
	smov(xp1,x,s);
	while (x[s] == ' ') s++;
	operator = x[s++];	/* Crank past op */
	while (x[s] == ' ') s++;
	smov(xp2,&x[s],10);	/* found now copy */
}

fault(num)		/* Inform carbon based unit of trouble */
int num;
{
	printf("**ERR: ");
	switch (num) {			/* Print verbals 	*/

		case 20: printf("Unknown mnemonic ");
			 break;
	        case 14: printf("Re-definition of %s ",label);
			 break;
		case 23: printf("Bad addressing mode, %s %s",mnem,operand);
			 break;
		case 210 : printf("Confused with %s %s %s ",label,mnem,operand);
			 break;
		case 215 : printf("Bad literal %s ",operand);
			 break;
		case 250:
		case 251: printf("Displacement out of range ");
			 break;
		case 600: printf("Cannot evaluate operand ");
			break;
		case 1  : printf("Illegal operator in <%s> ",operand);
			break;
		}

p_num:	printf(" <%03d> at line %u in module %s\n",num,lineno,iname);
	printf("%s\n",line);
	errors++;
	fprintf(obuf,";** ERR: %02d ",num);
 	fprintf(obuf,"PC: <%s> Mnem: <%s> Oprnd: <%s> ",labrec,mnem,operand);
	fprintf(obuf,"Class: <%c> I1: <%s> I2: <%s>",m4[0],i1,i2);
	fprintf(obuf,"I3: <%s>\n",i3);
	
}
